10
$\begingroup$

I have a special notebook styled for only creating functions and their packages. I often lazily do Evaluate Notebook instead of finding and opening all cells I need for testing code. For this to work I need to make certain that only relevant cells are styled for Input. I edited my stylesheet so that cells with CellStyle Code are never evaluated (with different colors) this way no symbols clash and bad or redundant definitions are prevented.

Because I usually only need to evaluate 2 or 3 cells at time it is best to change all Input CellStyle-s to Code then I can manually find and only change those Cells I need to run to CellStyle "Input".

So basically the function should search entire EvaluationNotebook[] for all cells with CellStyle \[Rule] "Input" and change each found with CellStyle \[Rule] "Code".

So far all 4 of my desperate attempts Failed although they all correctly found all Input Cells. I suspect I'm close but no dice.

First install needed resources...

ResourceFunction["PersistResourceFunction"]["PersistResourceFunction"]
PersistResourceFunction["SelectByCurrentValue"]

My 4 attempts...

NotebookFind[EvaluationNotebook[], "Input", All, CellStyle] /. 
 "Input" -> "Code"
SelectByCurrentValue[Cells[], Evaluatable] /. "Input" -> "Code"
cells = Cells[CellStyle -> "Input"]
CurrentValue[cells, CellStyle] = "Code"
Replace[#, "Input" -> "Code"] & /@ cells
$\endgroup$
5
  • 1
    $\begingroup$ What about using SetOptions to change the options Evaluatable and Background instead? $\endgroup$
    – Carl Woll
    Commented Nov 20, 2021 at 16:57
  • $\begingroup$ @CarlWoll although that is practical i would like to be able to do this for almost any CellStyle or other properties. so it's more of a learning exercise for me. $\endgroup$ Commented Nov 25, 2021 at 6:09
  • 1
    $\begingroup$ You can press Alt key (might be other keys on OS other than Windows) and left click the bracket of a certain cell to select all cells with the same style in the same notebook. $\endgroup$
    – Silvia
    Commented Dec 11, 2021 at 8:46
  • $\begingroup$ @Silvia wonderful tip. wolfram does a great job off making certain all keyboard shortcuts work for all operating systems even if keys are named differently. $\endgroup$ Commented Dec 11, 2021 at 10:35
  • 2
    $\begingroup$ Is this what you are after? Set the style of a cell $\endgroup$
    – Kuba
    Commented Dec 13, 2021 at 7:47

3 Answers 3

3
$\begingroup$

Fixing problems with your preferred solution

Edit: Solution 3 Refactored DOES NOT WORK AS EXPECTED
It only works in limited cases where I find Cells by their CellStyle and change CellStyle but not if I mix-n-match different properties for finding and changing. I don't know why that is. If you have insight please contribute.

Stricly speaking, the usage of All as the trird argument of Replace (as well as usage of ReplaceAll) is dangerous because it means that the replacement will be performed on all levels of the original Cell expression (returned by NotebookRead), while you wish to modify only the Rest of the latter. Hence the Solution 3 should be rewritten as follows in order to make it safe:

NotebookWrite[#, 
   Block[{cellExpr = NotebookRead[#]}, 
    Join[cellExpr[[1 ;; 1]], 
         Replace[Rest[cellExpr], "Input" -> "Code", {1}]]]] & /@ 
 Cells[CellStyle -> "Input"]

The reason why your replaceCellOps doesn't allow to "mix-n-match different properties for finding and changing" is that you designed it in the way which normally doesn't allow to do this. Here is a safe version which allows arbitrary replacements in the list of cell options:

Clear[replaceCellOptions]
replaceCellOptions[rules_, cells_] := 
  Block[{cellExpr = NotebookRead[#]}, 
     If[Head[cellExpr] === Cell, 
      NotebookWrite[#, 
       Join[cellExpr[[1 ;; 1]], 
        Replace[Rest[cellExpr], rules, {1}]]]]] & /@ cells;

Usage example (it will change styles of all "Input" cells to "Code" and prepend "Code " to their cell labels):

replaceCellOptions[{"Input" -> "Code", 
  HoldPattern[CellLabel -> label_] :> CellLabel -> "Code " <> label}, 
 Cells[CellStyle -> "Input"]]

Better solution: Stylesheet-based approach

An alternative solution which is both much more efficient and safe is to manupulate the private Notebook's stylesheet in order to change the style of cells, as shown here.

Replace "Input" style with "Code" style:

SetOptions[
    EvaluationNotebook[],
    StyleDefinitions -> Notebook[
        {
        Cell[StyleData[StyleDefinitions -> "Default.nb"]],
        Cell[StyleData["Input", StyleDefinitions -> StyleData["Code"]]]
        }
    ]
]

Now input cells are formatted with "Code" style, while in their definitions they still have style "Input":

screenshot

You can change arbitrary Cell options via private stylesheet. For example, make "Input" cells not just formatted with "Code" style, but also Italic:

SetOptions[EvaluationNotebook[], 
 StyleDefinitions -> 
  Notebook[{Cell[StyleData[StyleDefinitions -> "Default.nb"]], 
    Cell[StyleData["Input", StyleDefinitions -> StyleData["Code"]], 
     FontSlant -> Italic]}]]

If you wish to replace one of the "Code" style definitions for "Input" cells, you should place the "intercept" before the line Cell[StyleData["Input", StyleDefinitions -> StyleData["Code"]]. For example, making the font weight to be Plain (instead of Bold by default):

SetOptions[EvaluationNotebook[], StyleDefinitions -> Notebook[{
    Cell[StyleData[StyleDefinitions -> "Default.nb"]],
    Cell[StyleData["Input"], FontWeight -> Plain],
    Cell[StyleData["Input", StyleDefinitions -> StyleData["Code"]]],
    }]]

Reset everything to the defaults:

SetOptions[EvaluationNotebook[], StyleDefinitions -> "Default.nb"]

P.S. To force updating the styles of the notebook you may need to evaluate

FrontEndTokenExecute["ToggleShowExpression"]

or

NotebookClose[CreateNotebook[Visible -> False]]

as described here.

$\endgroup$
1
  • $\begingroup$ thank you for responding to this so quickly. I didnt expect to see a response in months if ever. This is also the only time ive ever seen HoldPattern used outside of General Tag Messages for errors. I cant wait to try it. Ill leave a comment sometime next week and give you input. :) $\endgroup$ Commented Dec 12, 2021 at 10:33
5
$\begingroup$

Try

Scan[(CurrentValue[#, StyleNames] = "Code")&, Cells[CellStyle -> "Input"]]

or

SetOptions[#, "Code"]& /@ Cells[CellStyle -> "Input" ]

or

NotebookWrite[#, Replace[NotebookRead[#],"Input" -> "Code", All]]& /@ 
  Cells[CellStyle -> "Input"]

or, using ReplaceAll,

NotebookWrite[#, ReplaceAll["Input"->"Code"] @ NotebookRead @ #]& /@ 
   Cells[CellStyle -> "Input"]

enter image description here

Update: The last 2 can be turned into functions with two optional arguments as follows:

ClearAll[replaceCellOps1, replaceCellOps2]
replaceCellOps1[from_: "Input", to_ : "Code"] := 
 Map[NotebookWrite[#, Replace[NotebookRead @ #, from -> to, All]] &]@
  Cells[CellStyle -> from]

replaceCellOps2[from_: "Input", to_ : "Code"] := 
 Map[NotebookWrite[#, ReplaceAll[from -> to] @ NotebookRead @ #] &]@
  Cells[CellStyle -> from]
$\endgroup$
3
  • $\begingroup$ it sort of kind of works. please view my answer for a detailed explanation and a suggestion on how it might be fixed. $\endgroup$ Commented Nov 20, 2021 at 15:40
  • 1
    $\begingroup$ (1) although 3rd solution works i have a habit of refactoring to a function that takes arguments for different cases of properties and values. here it is. i hope you like: replaceCellOps[from_ : "Input", to_ : "Code", cells_ : Cells[CellStyle -> "Input"]] := NotebookWrite[#, Replace[NotebookRead[#], from -> to, All]] & /@ cells; (2) i feel like i got real close with my 4 attempts using 3 with ReplaceAll and one with Map. how would you fix any of them. im dying to know. lol $\endgroup$ Commented Nov 20, 2021 at 18:23
  • 1
    $\begingroup$ @JulesManson, I like replaceCellOps; we need a small change to make it work in both directions (see my update). Re (2) I expected (CurrentValue[#, CellStyle] = "Code")&/@cells to work; I don't know why it doesn't. $\endgroup$
    – kglr
    Commented Nov 20, 2021 at 21:42
5
$\begingroup$

This is not a real answer but a detailed explanation of performance issues with member kglr's first two solutions. Use third solution. It works perfectly.

Solution 1: Scan

Scan[(CurrentValue[#, StyleNames] = "Code")&, Cells[CellStyle -> "Input"]

It sort of kind of works. Yes it seems to set CellStyle StyleNames from "Input" to "Code" by appending "Code" after "Input". So it doesn't actually remove (or replace) "Input". This can be seen with Cell -> Show Expression.

Original Input test Cell before invoking Scan[...]

Symbol@ToString@z
Head@z
z=2+2
Head@z

Show Expression of test Cell after Invoking Scan[...]...

Cell[BoxData[{
 RowBox[{"Symbol", "@", 
  RowBox[{"ToString", "@", "z"}]}], "\[IndentingNewLine]", 
 RowBox[{"Head", "@", "z"}], "\[IndentingNewLine]", 
 RowBox[{"z", "=", 
  RowBox[{"2", "+", "2"}]}], "\[IndentingNewLine]", 
 RowBox[{"Head", "@", "z"}]}], "Input", "Code",
 CellLabel->"In[18]:="]

This results in an Input Cell with all the properties and formats of Code Cells which are distinguishable by their StyleSheet settings for Background Color, Font Color, FontFace, etc and most importantly that Evaluatable -> False. In addition checking right-context menu for Style still shows Input highlighted. This solution does work when I dont want these cells evaluated.

However changing Code Cells back to Input which will eventually need to be done for most of them can be a little bit cumbersome. It requires 3 manual steps: (1) in right-click context menu set Style from Input to Code. (2) repeat in reverse: Code to Input. (3) from main menu do Format -> Clear Formatting.

Luckily kglr is a standup guy. He has since added two more solutions. I can't wait to try them. :)

Solution 2: SetOptions

Unfortunately this gives the very same results as Solution 1

SetOptions[#, "Code"]& /@ Cells[CellStyle -> "Input" ]

Solution 3: NotebookWrite

NotebookWrite[#, Replace[NotebookRead[#], "Input" -> "Code", All]] & /@
  Cells[CellStyle -> "Input"]

Third's are always a charm. This one works perfectly. Thank you so much kglr. I will add this as a Button for my DockedCell toolbar where I put my most essential functions. :)

Solution 3 Refactored: to broaden scope

I anchored it to a function so that it can accept almost any properties and values. That is how much I <3 this solution. :)

replaceCellOps[from_ : "Input", to_ : "Code", 
   cells_ : Cells[CellStyle -> "Input"]] := 
  NotebookWrite[#, Replace[NotebookRead[#], from -> to, All]] & /@ 
   cells;

Edit: Solution 3 Refactored DOES NOT WORK AS EXPECTED

It only works in limited cases where I find Cells by their CellStyle and change CellStyle but not if I mix-n-match different properties for finding and changing. I don't know why that is. If you have insight please contribute.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.